A computer program is a collection of computer instructions ordered in a manner such that when the program is executed by the computer the computer will produce certain results. Computer programs can be very simple, containing only a few instructions. Conversely, computer programs can be very complex, containing millions of instructions. For example, a certain type of computer program called an operating system can be extremely complex and often contains over a million instructions.
Most computer programs need to perform a certain task at several different places in the program. For example, an operating system may need to perform the task of writing to a display at many different places in the program. The programmer could simply repeat the same set of instructions at each place in the program. Having the same instructions duplicated throughout the program is usually undesirable for several reasons. If a change is made to the set of instructions, then the set must be changed wherever it is repeated in the program. Such changes may be time-consuming and error-prone. Also, having the same instructions repeated several times tends to make the program large, resulting in a program that may not fit into the memory of the computer.
The concept of a "subroutine" developed early in the history of computers. A subroutine is a set of instructions that is stored only once in a program, but can be "called" from several different places in a program. When a subroutine is called, the computer starts executing the instructions in the subroutine. When a "return" instruction in the subroutine is executed, the computer then "returns," that is, resumes execution of the program at the instruction after the call. In effect, the set of instructions is as if it was inserted into the program at the call.
Subroutines have several advantages over the duplicating of the set of instructions. Subroutines save space in the program, since the set of instructions is stored only once. Subroutines make it easier to organize complex programs. Subroutines make it easier to make changes to the set of instructions, since the changes need only be made once.
The state-of-the-art computers, such as those based on Intel 8086-family microprocessors, have special-purpose instructions to facilitate the calling of and returning from subroutines. Specifically, there are two instructions for calling and two instructions for returning.
The Intel 8086-family of microprocessors uses a segmented addressing architecture. One such microprocessor is the Intel 80386, which is manufactured by the Intel Corporation of Santa Clara, Calif. The design and operation of the Intel 80386 is described in detail in a publication entitled "iAPX 386 Programmer's Reference Manual Including the iAPX 386 Numeric Supplement," which is available from Intel Corporation and is hereby incorporated by reference. The Intel 80386 has one call instruction and one return instruction that are for invoking an intra-segment (a near) subroutine and another call instruction and return instruction that are for invoking an inter-segment (a far) subroutine.
The addresses in the 8086-family comprise a segment and an offset. Each call and return must specify a segment and an offset. When a call or return is executed, the microprocessor combines the segment and the offset to form an address. The call instruction forms the address of the subroutine; the return instruction forms the address of the instruction to return to. The methods of calling and returning from subroutines is referred to as subroutine "linkage."
The near call and return are designed to be used when the call instruction and called subroutine are within the same segment when executed, that is, the current code segment. The code segment (CS) register contains information pointing to the current code segment. When a near call is executed, the processor pushes onto the stack the offset in the current segment of the instruction after the call instruction. The processor then jumps to the subroutine, which is located at a specified offset in the current code segment. When the subroutine returns, it executes a near return instruction. When the near return instruction is executed, it pops the offset, which was pushed onto the stack by the near call instruction, from the stack. The processor then forms an address based on current code segment and the offset. This address is the address of the instruction after the near call instruction. The processor then proceeds to execute the instruction at that address.
The limitation of the near call and return instructions is that only subroutines within the same segment can be called. The far call and return instructions have no such limitation.
The far call and return instructions work similarly to their near counterparts, except that these instructions push and pop both a segment and offset component. The far call specifies both a segment and an offset for the subroutine. Consequently, the current setting of the CS register is saved on the stack in addition to the offset of the instruction after the call. When the far return instruction is executed, the processor pops the segment and offset values pushed by the corresponding call instruction, forms an address, and returns to that address. The far call can specify any address and, unlike the near call, is not limited to those addresses within the current code segment.
A typical subroutine is designed to be called either by a near call instruction or a far call instruction. If designed to be called by a near call instruction, then the subroutine executes a near return instruction, which pops only the offset, to return. If designed to be called by a far call instruction, then the subroutine executes a far return instruction, which pops both the segment and offset, to return. If a subroutine that is designed to be called by a far call is invoked by a near call, then the subroutine will not return to the instruction after the far call. This mixing of near call with far return or mixing of a far call with a near return will result in a program error.
Several methods have been used for allowing a subroutine to be invoked by both a near and a far call instruction. One of these methods of subroutine linkage uses different entry points when the subroutine is invoked by a near call and a far call.
It would be desirable to have an improved method for subroutine linkage so that a subroutine can be invoked by either a far call or a near call.